package org.dding.msc.service.impl;

import static java.lang.Thread.holdsLock;

import org.dding.msc.exception.DuplicateServiceException;
import org.dding.msc.service.ServiceContainer;
import org.dding.msc.service.ServiceController;
import org.dding.msc.service.ServiceName;
import org.dding.msc.service.ServiceRegistration;

/**
 * A single service registration.
 * 
 */
final class ServiceRegistrationImpl implements ServiceRegistration {

	/**
	 * The service container which contains this registration.
	 */
	private final ServiceContainer container;
	/**
	 * The name of this registration.
	 */
	private final ServiceName name;

	/**
	 * The current instance.
	 */
	private ServiceController<?> instance;

	ServiceRegistrationImpl(final ServiceContainer container,
			final ServiceName name) {
		this.container = container;
		this.name = name;
	}

	/**
	 * Set the instance.
	 * 
	 * @param instance
	 *            the new instance
	 * @throws DuplicateServiceException
	 *             if there is already an instance
	 */
	@Override
	public void setInstance(final ServiceController<?> instance)
			throws DuplicateServiceException {
		assert instance != null;
		assert !holdsLock(this);
		assert !holdsLock(instance);
		synchronized (this) {
			if (this.instance != null) {
				throw new DuplicateServiceException(String.format(
						"Service %s is already registered",
						name.getCanonicalName()));
			}
			this.instance = instance;
		}
	}

	@Override
	public void clearInstance(final ServiceController<?> oldInstance) {
		assert !holdsLock(this);
		synchronized (this) {
			final ServiceController<?> instance = this.instance;
			if (instance != oldInstance) {
				return;
			}
			this.instance = null;
		}
	}

	@Override
	public ServiceContainer getContainer() {
		return container;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.dding.msc.service.impl.ServiceRegistration#getName()
	 */
	public ServiceName getName() {
		return name;
	}

	@Override
	public Object getValue() throws IllegalStateException {
		synchronized (this) {
			final ServiceController<?> instance = this.instance;
			if (instance == null) {
				throw new IllegalStateException("Service is not installed");
			} else {
				return instance.getValue();
			}
		}
	}

	public ServiceController<?> getInstance() {
		synchronized (this) {
			return instance;
		}
	}
}
